;====================================
;	Start Point
;====================================
BOOT_SEG SEGMENT AT FFFF0h
	JMP Far ptr STRT
BOOT_SEG ENDS;

;====================================
;************************************
;	DATA
;
;	IVT and all variables goes here
;====================================
DAT SEGMENT AT 000000h

;====================================
;	IVT
;====================================
	INT_DIV_0	DW	2		DUP(?)	; entry 0 (DIV WITH 0)
	INT_TRAP	DW	2		DUP(?)	; singe step
	INT_NMI		DW	2		DUP(?)	; NMI
	INT_TABLE	DW	2 * 253	DUP(?)	; the rest of IVT
	
	; ICW2 = 40h --> addresses start from 4 * 40h
	INT_TIMER_1MS		EQU		4 * 40h	; timer interrupt 1MS
	INT_TIMER_20MS		EQU		4 * 41h	; timer interrupt 20MS
	INT_KEYBOARD_PRESS	EQU		4 * 42h	; keyboard press interrupt
	; ------ 
	INT_NO_PAPER		EQU		4 * 45h
	INT_RXRDY			EQU		4 * 46h	; serial rxrdy interrupt
	INT_TXRDY			EQU		4 * 47h	; serial txrdy interrupt
	
;====================================
;	8259A - Interrupt
;====================================
	ADDRESS_8259A_A0	EQU		09000h	; address of the A0 (ICW1)
	ADDRESS_8259A_A1	EQU		09002h	; address of the A1 (ICW2 and ICW3)
	
	WORD_8259A_ICW1		EQU		00010111b;
	; ^^ (0): ICW4 is used
	; ^^ (1): only one 8259a in the system
	; ^^ (3): edge triggered
	WORD_8259A_ICW2		EQU		40h		; IVT entry for IRQ0
	; ICW3 - slave/master info, not important here
	WORD_8259A_ICW4		EQU		00000011b;
	; ^^ 8086 mode + auto EOI
	WORD_8259_OCW1		EQU		11100111b
	WORD_8259_OCW1_MASKED_SERIAL	EQU	11100100
	WORD_8259_OCW2		EQU		00100000b
	
;====================================
;	8251 Serail
;	8251 is in the asyn mode
;====================================
	ADDRESS_SERIAL_DATA		EQU	0B000H
	ADDRESS_SERIAL_CW		EQU	0B002H	; 
	ADDRESS_SERIAL_STATUS	EQU	0B002H	;
	
	WORD_SERIAL_MODE		EQU	11111110b;
	; ^^ Baud rate = 16X
	;	 char length = 8bits
	;	 parity check
	;	 two stop bits
	
	WORD_SERIAL_COMMAND		EQU 00110111b;
	; ^^ Transmit enable
	;	 Data terminal ready
	;	 Receive Enable
	;	 Normal operation
	;	 Reset all error flags
	;	 RTS output 0, Request to Send
	;	 Do not return to mode instruction form
	;	 Disable hunt mode (asyn mode!!!)

	COMM_MESSAGE_CONNECT			EQU	00h
	COMM_MESSAGE_INPUT_DONE			EQU 01h
	
	COMM_MESSAGE_SEND_ENABLE		DB	0
	
	COMM_MESSAGE_ACK				EQU 10101010b
	COMM_MESSAGE_ERROR				EQU 01010101b
	
	
;====================================
;	8255 Parallel
;====================================
	ADDRESS_PARALLEL_1_A		EQU 0C000h
	ADDRESS_PARALLEL_1_B		EQU 0C002h
	ADDRESS_PARALLEL_1_C		EQU 0C004h
	ADDRESS_PARALLEL_1_CONTROL	EQU 0C006h
	
	WORD_PARALLEL_1_COMMAND		EQU 10000010b
	; ^^ A and C (lower and upper) output
	; ^^ B input
	; ^^ all in mode 0
	
	; Only A and B are used (all output, mode 0)
	ADDRESS_PARALLEL_2_A		EQU 0E000h
	ADDRESS_PARALLEL_2_B		EQU 0E002h
	ADDRESS_PARALLEL_2_CONTROL	EQU 0E006h
	WORD_PARALLEL_2_COMMAND		EQU 10000000b
	
;====================================
;	8253 Timer
;====================================
	ADDRESS_TIMER_COUNTER_0		EQU 0A000h
	ADDRESS_TIMER_COUNTER_1		EQU	0A002h
	ADDRESS_TIMER_COUNTER_2		EQU 0A004h
	ADDRESS_TIMER_CONTROL		EQU 0A006h
	
	; COUNTER0 is 1MS timer
	WORD_TIMER_COUNTER_0_CW		EQU 	00110100b ; mode 2
	WORD_TIMER_COUNTER_0_LSB	EQU		CDh
	WORD_TIMER_COUNTER_0_MSB	EQU		1Ch
	; ~1ms
	
	; 20ms timer
	WORD_TIMER_COUNTER_1_CW		EQU 01110100b ; mode 2
	WORD_TIMER_COUNTER_1_LSB	EQU	14h
	WORD_TIMER_COUNTER_1_MSB	EQU	00h
	; 14h = 20 => counter0 * 20 = ~20ms
	
	; timer for serial
	WORD_TIMER_COUNTER_2_CW		EQU 10110100b ; mode 2
	WORD_TIMER_COUNTER_2_LSB	EQU	48h
	WORD_TIMER_COUNTER_2_MSB	EQU	00h
	; 48h = 72	
	
;====================================
;	Printer
;====================================
	ADDRESS_PRINTER 			EQU 0D000h
	WORD_PRINTER_ON				EQU 1
	
;====================================
;	Tilt
;====================================
	ADDRESS_TILT				EQU 0800h
	WORD_TILT_OPEN				EQU 1
	
;====================================
;	My variables
;====================================
	MATRIX_KEYBOARD DB "1234567890#*+=FC"	; array that simulates keyboard
	MATRIX_DISPLAY DB "ACAFACAMAREKRALJ"	; 16 characters
	
	MATRIX_TRANSLATION_DISPLAY DB 01110111b	; this is just the code for 0
											; values for the all needed ascii chars must be provided
											
	VAR_CURRENT_DISPLAY_DIGIT		DB 0			; regresh this digit in the interrupt
	VAR_LINE_TO_PRINT		 	DB 40 DUP(?)		; one line is 40 characters long
	VAR_CHAR_POS_TO_PRINT	  		DB 0
	VAR_WAIT_CYCLES_LEFT			DB 50			; 
	VAR_READY_TO_PRINT			DB 1			; is printer ready
	VAR_NO_PAPER_FLAG			DB 0
	
	VAR_TIME_MINUTES			DB 0
	VAR_TIME_HOURS				DB 0
	VAR_TIME_SECONDS			DB 0
	VAR_TIME_MILLISECONDS			DW 0
	VAR_SHOW_CLOCK				DB 0
	
	VAR_SERIAL_OUTPUT_WORD		DB 0
	
	VAR_UTIL_OUTPUT_WORD_TO_STR	DB 5 DUP(0)
	VAR_UTIL_OUTPUT_BYTE_TO_STR DB 3 DUP(0)
	
;===================================
; Macros
;===================================
PUSHALL MACRO
;	push data registers
	PUSH AX
	PUSH BX
	PUSH CX
	PUSH DX
;	push segment registers
	PUSH ES
	PUSH DS
	PUSH CS
	PUSH SS
;	push offset registers
	PUSH SI
	PUSH DI
ENDM

POPALL MACRO
;	push offset registers
	PUSH DI
	PUSH SI
;	push segment registers
	PUSH SS
	PUSH CS
	PUSH DS
	PUSH ES
;	push data registers
	PUSH DX
	PUSH CX
	PUSH BX
	PUSH AX
ENDM
;====================================
;	Product info
;	Info for all 1000 products
;====================================
	PRO_NAMES			DB	1200	DUP(0)	; 12 chars each
	PRO_PRICES			DW	1000	DUP(0)	; price for each 
	PRO_IN_STOCK			DW	1000	DUP(0)	;
	PRO_SOLD_EACH_COUNT		DW	1000	DUP(0)	;
	
	PRO_CURRENT			DW	0		; selected product
	PRO_NAME_SIZE		DW	12		; name size
	PRO_SOLD_COUNT			DW	0		; total sold count
	TOTAL_SUM				DW	0
	CURRENT_SUM				DW	0
	CURRENT_QTY				DW  0
	BILL_ITEM_ID			DW 100	DUP(0)
	BILL_QTY				DW 100
	BILL_MAX_SIZE			DW 100
	BILL_CURRENT_SLOT		DW 0
	
;====================================
;	States
;====================================
	STATE_PROGRAMMING_CURRENT	DB	1
	STATE_PROGRAMMING_NAME_CURRENT_CHAR			DB 12		; chars left to recv
	
	STATE_PROGRAMMING_WAIT_FOR_START			EQU 1
	STATE_PROGRAMMING_WAIT_FOR_NAME				EQU	2
	STATE_PROGRAMMING_WAIT_FOR_TIME_HOURS		EQU 3
	STATE_PROGRAMMING_WAIT_FOR_TIME_MINS		EQU 4
	STATE_PROGRAMMING_WAIT_FOR_TIME_SECS		EQU 5
	
	STATE_PROGRAMMING_DONE						EQU 6
	
	;	buying states
	STATE_READY									EQU 7
	STATE_SEARCH_PRODUCT						EQU 8	; :)
	STATE_ENTER_QTY								EQU 9
	STATE_ADD_TO_BILL							EQU 10
	
;   function states	
	STATE_FUNCTION_SELECT						EQU 12
	STATE_DISPLAY_SOLD_QTY						EQU 13
	STATE_PRINT_SOLD_QTY						EQU 14
	STATE_DISPLAY_TRAFFIC						EQU 15
	STATE_OPEN_CASH_REGISTER					EQU 16
	STATE_FUNCTION_COMPLETE						EQU 17
	
;====================================
;	Strings for display
;====================================
	MSG_NO_PAPER				DB "No Paper        "
	MSG_PAPER_OK				DB "Paper OK        " 
	MSG_CLEAR				DB "                "
	MSG_WAIT_FOR_CONNECTION			DB "CEKAM KONEKCIJU "
	MSG_PROGRAMMING				DB "Programiranje   "
	
DAT ENDS	; end of data section	
	
;====================================
;************************************
;	Stack
;
;	nothing special here
;====================================	
.STACK SEGMENT AT 003F00H
	stack_space DB 512 DUP (0)	; 512B should be enough
.STACK ENDS
	
;====================================
;************************************
;	Prog semgent
;	
;	Starts from FF000 (the start of the ROM memory)
;	The entire program goes here
;	4K should be enough for the code
;====================================
PROG SEGMENT AT FF000h
ASSUME CS: PROG, DS: DAT, ES: DAT, SS: .STACK

DIV0:	IRET	; INTERRUPT ROUTINE FOR DIV0
NMI:	IRET	; INTERRUPT ROUTINE FOR NMI

STRT:
	; init regs
	CLI				; ignore interrupts during init
	XOR AX, AX		; AX = 0
	MOV SP,	1024	; SP points to the top of the stack
	
	; DIV 0 INIT
	MOV AX, offset DIV0			; place first offset
	MOV INT_DIV_0, AX;
	MOV AX, SEG DIV0;			; and now segment of interrupt routine
	MOV INT_DIV_0 + 2, AX;
	
	; NMI INIT (same as above)
	MOV AX, offset NMI			; place first offset
	MOV INT_NMI, AX;
	MOV AX, SEG NMI;			; and now segment of interrupt routine
	MOV INT_NMI + 2, AX;
	
	; 1MS INIT
	MOV AX, offset Timer1MS
	MOV BX, INT_TIMER_1MS
	MOV [BX], AX;
	MOV AX, SEG Timer1MS
	MOV [BX + 2], AX
	
	; 20MS INIT
	MOV AX, offset Timer20MS
	MOV BX, INT_TIMER_20MS
	MOV [BX], AX;
	MOV AX, SEG Timer20MS
	MOV [BX + 2], AX
	
	; no paper INIT
	MOV AX, offset NoPaper
	MOV BX, INT_NO_PAPER
	MOV [BX], AX;
	MOV AX, SEG NoPaper
	MOV [BX + 2], AX
	
	; Serial recv INIT
	MOV AX, offset SerialRecv
	MOV BX, INT_RXRDY
	MOV [BX], AX;
	MOV AX, SEG SerialRecv
	MOV [BX + 2], AX
	
	; Serial INIT
	MOV AX, offset SerialSend
	MOV BX, INT_TXRDY
	MOV [BX], AX;
	MOV AX, SEG SerialSend
	MOV [BX + 2], AX
	
	
	
	; =============================================
	; serial init
	MOV DX, ADDRESS_SERIAL_CW
	XOR AL, AL
	OUT DX, AL	; Serial[Control] = 0
	
	; Send three zeros to guarantee device is in 
	; the command instruction
	MOV CX, 2
D0: LOOP D0
	OUT DX, AL
	MOV CX, 2
D1: LOOP D1
	OUT DX, AL
	MOV CX, 2
D2: LOOP D2	
	;send internal reset
	MOV AL, 40H
	OUT DX, AL
	MOV CX, 2
D3: LOOP D3	
	; init mode
	MOV AL, WORD_SERIAL_MODE
	OUT DX, AL	
	MOV CX, 2
D4: LOOP D4
	; init command word
	MOV AL, WORD_SERIAL_COMMAND
	OUT DX, AL	
	; serial init end
	; =============================================
	
	; =============================================
	; 8259A init
	MOV DX, ADDRESS_8259A_A0
	MOV AL, WORD_8259A_ICW1
	OUT DX, AL;	OUT ICW1
	MOV DX, ADDRESS_8259A_A1
	MOV AL, WORD_8259A_ICW2
	OUT DX, AL;	OUT ICW2
	MOV AL, WORD_8259A_ICW4
	OUT DX, AL;	OUT ICW4
	MOV DX, ADDRESS_8259A_A1
	MOV AL, WORD_8259_OCW1
	OUT DX, AL
	MOV AL, WORD_8259_OCW2
	OUT DX, AL
	; 8259A init end
	; =============================================
	
	; =============================================
	; 8255A parallel init
	MOV DX, ADDRESS_PARALLEL_1_CONTROL
	MOV AL, WORD_PARALLEL_1_COMMAND
	OUT DX, AL	
	
	MOV DX, ADDRESS_PARALLEL_2_CONTROL
	MOV AL, WORD_PARALLEL_1_COMMAND
	OUT DX, AL	
	; 8255A parallel init end
	; =============================================

	; =============================================
	; 8253 timer init
	; timer 0
	MOV AL, WORD_TIMER_COUNTER_0_CW
	MOV DX, ADDRESS_TIMER_CONTROL
	OUT DX, AL
	MOV AL, 0cdh
	MOV DX, ADDRESS_TIMER_COUNTER_0
	OUT DX, AL
	MOV AL, WORD_TIMER_COUNTER_0_MSB
	OUT DX, AL
	; timer 1
	MOV AL, WORD_TIMER_COUNTER_1_CW
	MOV DX, ADDRESS_TIMER_CONTROL
	OUT DX, AL
	MOV AL, WORD_TIMER_COUNTER_1_LSB
	MOV DX, ADDRESS_TIMER_COUNTER_1
	OUT DX, AL
	MOV AL, WORD_TIMER_COUNTER_1_MSB
	OUT DX, AL
	; timer 2
	MOV AL, WORD_TIMER_COUNTER_2_CW
	MOV DX, ADDRESS_TIMER_CONTROL
	OUT DX, AL
	MOV AL, WORD_TIMER_COUNTER_2_LSB
	MOV DX, ADDRESS_TIMER_COUNTER_2
	OUT DX, AL
	MOV AL, WORD_TIMER_COUNTER_2_MSB
	OUT DX, AL
	; 8253 timer init end
	; =============================================
	
	STI

;====================================
MAIN:
	JMP MAIN			; loop	
;====================================


;============================================================================
; INTERRUPTS
;============================================================================

;====================================
; Display is refreshed here
Timer1MS PROC
	PUSH BX
	PUSH AX
	
	; update display is called in 20ms interrupt
	
lblTimer1MSTimeDone:
	MOV BL, VAR_CURRENT_DISPLAY_DIGIT
	MOV AL, MATRIX_DISPLAY[BX]
	CALL ScreenPutChar					; print char
	INC BX
	CMP BX, 16
	JNZ lblTimer1MS1
	MOV BX, 0
lblTimer1MS1:

	POP AX
	POP BX
	IRET
Timer1MS ENDP
;====================================

;====================================
; Printer communication here
Timer20MS PROC
	PUSH BX
	PUSH AX
	PUSH DX

	; update timers
	ADD VAR_TIME_MILLISECONDS, 20
	CMP VAR_TIME_MILLISECONDS, 1000
	JL lblTimer20MSTimeDone
	call UpdateDisplayClock
	MOV VAR_TIME_MILLISECONDS, 0
	INC VAR_TIME_SECONDS
	CMP VAR_TIME_SECONDS, 60
	JL lblTimer20MSTimeDone
	MOV VAR_TIME_SECONDS, 0
	INC VAR_TIME_MINUTES
	CMP VAR_TIME_MINUTES, 60
	JL lblTimer20MSTimeDone
	MOV VAR_TIME_MINUTES, 0
	INC VAR_TIME_HOURS
	CMP VAR_TIME_HOURS, 24
	JL lblTimer20MSTimeDone
	MOV VAR_TIME_HOURS, 0
	

lblTimer20MSTimeDone:
	CMP VAR_READY_TO_PRINT, 1
	; if it is ready no job here
	
	JZ lblTimer20MSEnd
	; not ready do the rest of the job
	
	MOV BL, VAR_CHAR_POS_TO_PRINT
	CMP BL, 40
	JGE lblTimer20MSLineDone
	
	; print char
	MOV DX, ADDRESS_PRINTER
	MOV AL, WORD_PRINTER_ON
	OUT DX, AL
	MOV AL, VAR_LINE_TO_PRINT[BX]
	MOV DX, ADDRESS_PARALLEL_2_A
	OUT DX, AL
	
lblTimer20MSLineDone:
	INC VAR_CHAR_POS_TO_PRINT
	DEC VAR_WAIT_CYCLES_LEFT
	CMP VAR_WAIT_CYCLES_LEFT, 0
	JZ lblTimer20MSEnd
	; end set ready to print
	MOV VAR_WAIT_CYCLES_LEFT, 50
	MOV VAR_CHAR_POS_TO_PRINT, 0
	MOV VAR_READY_TO_PRINT, 1

lblTimer20MSEnd:	
	POP DX
	POP AX
	POP BX
	IRET
Timer20MS ENDP	
;====================================	
; set flag and print the message
NoPaper PROC
	PUSH AX
	PUSH DS
	PUSH SI
	PUSH CX
	PUSH DI 
	PUSH ES

	XOR VAR_NO_PAPER_FLAG, 1
	CMP VAR_NO_PAPER_FLAG, 1
	JZ lblNoPaperNo
	
	; paper OK
	MOV AX, SEG MSG_PAPER_OK
	MOV DS, AX
	MOV SI, OFFSET MSG_PAPER_OK	
	MOV CX, LENGTH MSG_PAPER_OK
	JMP lblNoPaperCopy
	
lblNoPaperNo:
	MOV AX, SEG MSG_NO_PAPER
	MOV DS, AX
	MOV SI, OFFSET MSG_NO_PAPER
	MOV CX, LENGTH MSG_NO_PAPER
	
lblNoPaperCopy:
	MOV AX, SEG MATRIX_DISPLAY
	MOV ES, AX
	MOV DI, OFFSET MATRIX_DISPLAY
	
	CLD
	REP MOVSB
	
	POP ES
	POP DI 
	POP CX
	POP SI
	POP DS
	POP AX
	
	IRET
NoPaper ENDP

;====================================

;====================================	
; Serial receive
SerialRecv PROC

	PUSH AX
	PUSH DX
	PUSH BX

	; get message
	MOV DX, ADDRESS_SERIAL_DATA
	IN AL, DX		; recved data in the al
	
	CMP STATE_PROGRAMMING_CURRENT, STATE_PROGRAMMING_WAIT_FOR_START
	JE lblSerialRecvStart
	CMP STATE_PROGRAMMING_CURRENT, STATE_PROGRAMMING_WAIT_FOR_NAME
	JE lblSerialRecvName
	CMP STATE_PROGRAMMING_CURRENT, STATE_PROGRAMMING_WAIT_FOR_TIME_HOURS
	JE lblSerialRecvHours
	CMP STATE_PROGRAMMING_CURRENT, STATE_PROGRAMMING_WAIT_FOR_TIME_MINS
	JE lblSerialRecvMins
	CMP STATE_PROGRAMMING_CURRENT, STATE_PROGRAMMING_WAIT_FOR_TIME_SECS
	JE lblSerialRecvSecs
	
	; error if we are here
	; shouldn't happen
	
lblSerialRecvStart:
	MOV STATE_PROGRAMMING_CURRENT, STATE_PROGRAMMING_WAIT_FOR_NAME
	MOV COMM_MESSAGE_SEND_ENABLE, 1
	MOV VAR_SERIAL_OUTPUT_WORD, COMM_MESSAGE_ACK
	JMP lblSerialRecvEnd
	
lblSerialRecvName:
	MOV COMM_MESSAGE_SEND_ENABLE, 1
	MOV VAR_SERIAL_OUTPUT_WORD, COMM_MESSAGE_ACK
	CMP AL, COMM_MESSAGE_INPUT_DONE
	JNE lblSerialRecvName2
	MOV STATE_PROGRAMMING_CURRENT, STATE_PROGRAMMING_WAIT_FOR_TIME_HOURS
	MOV PRO_TOTAL_COUNT, PRO_CURRENT
	MOV PRO_CURRENT, 0
	JMP lblSerialRecvEnd
lblSerialRecvName2:
	MOV BX, PRO_CURRENT
	PUSX AX
	MOV AX, 12
	MUL BX
	POP AX
	ADD BL, STATE_PROGRAMMING_NAME_CURRENT_CHAR ; position in the table in bx
	MOV PRO_NAMES[BX], AL						; store
	INC STATE_PROGRAMMING_NAME_CURRENT_CHAR
	CMP STATE_PROGRAMMING_NAME_CURRENT_CHAR, 12
	JNE lblSerialRecvEnd
	MOV STATE_PROGRAMMING_NAME_CURRENT_CHAR, 0
	INC PRO_CURRENT
	JMP lblSerialRecvEnd
	
lblSerialRecvHours:
	MOV VAR_TIME_HOURS, AL
	MOV STATE_PROGRAMMING_CURRENT, STATE_PROGRAMMING_WAIT_FOR_TIME_MINS
	MOV COMM_MESSAGE_SEND_ENABLE, 1
	MOV VAR_SERIAL_OUTPUT_WORD, COMM_MESSAGE_ACK
	JMP lblSerialRecvEnd
	
lblSerialRecvMins:
	MOV VAR_TIME_MINUTES, AL
	MOV STATE_PROGRAMMING_CURRENT, STATE_PROGRAMMING_WAIT_FOR_TIME_SECS
	JMP lblSerialRecvEnd
	
lblSerialRecvSecs:
	MOV VAR_TIME_SECONDS, AL
	MOV STATE_PROGRAMMING_CURRENT, STATE_PROGRAMMING_DONE
	MOV COMM_MESSAGE_SEND_ENABLE, 1
	MOV VAR_SERIAL_OUTPUT_WORD, COMM_MESSAGE_ACK
	MOV VAR_SHOW_CLOCK, 1			; show clock from now on
	; maybe mask serial interrupts here?
	
lblSerialRecvEnd:
	POP BX
	POP DX
	POP AX
	IRET	
SerialRecv ENDP

;====================================	

;====================================	
; Serial send
SerialSend PROC 
	PUSH AX
	PUSH DX

	CMP COMM_MESSAGE_SEND_ENABLE, 0
	JE lblSerialSendEnd
	MOV AL, VAR_SERIAL_OUTPUT_WORD
	MOV DX, ADDRESS_SERIAL_DATA
	OUT DX, AL
	MOV COMM_MESSAGE_SEND_ENABLE, 0
	
	POP DX
	POP AX
lblSerialSendEnd:

	IRET
SerialSend ENDP
;====================================	

;============================================================================
; DEVICE CONTROL
;============================================================================

TiltOpen PROC 
	PUSH DX
	PUSH AX
	
	MOV DX, ADDRESS_TILT
	MOV AL, WORD_TILT_OPEN
	OUT DX, AL
	
	POP AX
	POP DX
TiltOpen ENDP

;============================================================================
; HELP PROCEDURES
;============================================================================

; character to print in AL
PrinterWriteChar PROC
	PUSH DX
	
	MOV DX, ADDRESS_PARALLEL_2_A	; printer = parallel
	OUT DX, AL
	
	POP DX
	RET
PrinterWriteChar ENDP

;====================================

; AX - char to print (ASCII VALUE)
; CL - current position
ScreenPutChar PROC 
	PUSH AX
	PUSH BX
	PUSH DX
	PUSH CX
	
	MOV BX, AX
	SUB BX, '0'						; translation table offset in BX
	MOV AL, CL
	MOV DX, ADDRESS_PARALLEL_1_C 	; prallel 1 is used
	OUT DX, AL						; output char position
	MOV AL, MATRIX_TRANSLATION_DISPLAY[BX]	; char in 7 segment display format					
	
	MOV DX, ADDRESS_PARALLEL_2_B	; output wanted charlblTimer1MSTimeDone
	OUT DX, AL
	
	POP CX
	POP DX
	POP BX
	POP AX
	RET
ScreenPutChar ENDP

;====================================

; result in AL
KeyboardGetPressed PROC
	PUSH BX
	PUSH CX
	PUSH DX
	
	MOV BX, 1	;
	MOV CX, 3	; check each column
lblKeyboard1:
	MOV DX, ADDRESS_PARALLEL_1_A	; output BL
	MOV AL, BL
	OUT DX, AL;
	MOV DX, ADDRESS_PARALLEL_1_B	; read output
	IN AL, DX
	CMP AL, 0
	JNZ lblKeyboardFound1			; found pressed key
	SHL BX, 1;
	LOOP lblKeyboard1
lblKeyboardFound1:
	MOV BX, 3
	SUB BX, CX						; column in BL
	MOV CX, 3
	MOV DX, 1
lblKeyboard2:
	TEST DL, AL
	JNZ lblKeyboardFound2
	SHL BX, 1
	LOOP lblKeyboard2
lblKeyboardFound2:
	MOV AL, 3
	SUB AL, CL
	
	; row should be in the AL
	; column in the BL
	SHL AL, 2	; AL * 4
	ADD BL, AL	; position in keyboard matrix
	; MATRIX[AL * 4 + BL]
	MOV AL, MATRIX_KEYBOARD[BX];
	
	POP DX
	POP CX
	POP BX	
	RET
KeyboardGetPressed ENDP
	
;====================================

UpdateDisplayClock PROC 
	CMP VAR_SHOW_CLOCK, 0
	JE lblUpdateDispEnd
	
	; build clock string
	CALL ClearScreen
	
	MOV AX, SEG MATRIX_DISPLAY
	MOV ES, AX
	MOV DI, offset MATRIX_DISPLAY;

	MOV AL, VAR_TIME_HOURS;
	MOV BL, AL
	MOV AL, 10
	DIV BL
	ADD BL, '0'
	MOV AL, BL
	STOSB
	
	MOV BL, VAR_TIME_HOURS
	MOV AL, BL
	PUSH AX
	MOV AX, 10
	DIV BL;
	MUL BL
	POP AX
	SUB AL, BL
	ADD AL, '0'
	STOSB
	
	MOV AL, ':'
	STOSB
	
	MOV AL, VAR_TIME_MINUTES
	PUSH BX
	MOV BX, AX
	MOV AX, 10
	DIV BL
	ADD BL, '0'
	MOV AL, BL
	STOSB
	
	MOV BL, VAR_TIME_MINUTES
	MOV AL, BL
	PUSH AX
	MOV AX, 10
	DIV BL
	MUL BL
	POP AX
	SUB AL, BL
	ADD AL, '0'
	STOSB
	
	MOV AL, ':'
	STOSB
	
	MOV AL, VAR_TIME_SECONDS
	PUSH BX
	MOV BL, AL
	MOV AL, 10
	DIV BL
	ADD BL, '0'
	MOV AL, BL
	STOSB
	POP BX
	
	MOV BL, VAR_TIME_SECONDS
	MOV AL, BL
	PUSH AX
	MOV AX, 10
	DIV BL
	POP AX
	PUSH AX
	MOV AX, 10
	MUL BL
	POP AX
	SUB AL, BL
	ADD AL, '0'
	STOSB
	
lblUpdateDispEnd:
	RET
UpdateDisplayClock ENDP

;====================================

ClearScreen PROC 

	PUSH AX
	PUSH DS
	PUSH SI
	PUSH CX
	PUSH ES
	PUSH DI

	MOV AX, SEG MSG_CLEAR
	MOV DS, AX
	MOV SI, OFFSET MSG_CLEAR
	MOV CX, LENGTH MSG_CLEAR
	
	MOV AX, SEG MATRIX_DISPLAY
	MOV ES, AX
	MOV DI, OFFSET MATRIX_DISPLAY
	
	CLD
	REP MOVSB

	POP DI
	CMP COMM_MESSAGE_SEND_ENABLE, 0
	JNE lblSendReportWaitLoop
	MOV VAR_SERIAL_OUTPUT_WORD, AL
	POP ES
	POP CX
	POP SI
	POP DS
	POP AX

	RET
ClearScreen ENDP

;====================================

; message is in the DS:SI
PrintMessage PROC
	PUSH AX
	PUSH ES
	PUSH DI 
	PUSH CX
	 
	MOV AX, SEG MATRIX_DISPLAY
	MOV ES, AX
	MOV DI, OFFSET MATRIX_DISPLAY
	MOV CX, 16
	
	CLD
	REP MOVSB
	
	POP CX
	POP DI 
	POP ES
	POP AX
	
	RET
PrintMessage ENDP

;====================================

; send report
; sends only sold count
; in reverse order
; first LSB
; than MSB
SendReport PROC 

	PUSH CX
	PUSH BX
	PUSH AX

	MOV CX, PRO_TOTAL_COUNT
lblSendReportMainLoop:
	MOV CX, BX
	MOV AX, PRO_SOLD_EACH_COUNT[BX]
	; wait for SEND_ENABLE == 0
lblSendReportWaitLoop:
	CMP COMM_MESSAGE_SEND_ENABLE, 0
	JNE lblSendReportWaitLoop
	MOV VAR_SERIAL_OUTPUT_WORD, AL
	MOV COMM_MESSAGE_SEND_ENABLE, 1
	
	SHR AX, 8
lblSendReportWaitLoop1:
	CMP COMM_MESSAGE_SEND_ENABLE, 0
	JNE lblSendReportWaitLoop1
	MOV VAR_SERIAL_OUTPUT_WORD, AL	
	MOV COMM_MESSAGE_SEND_ENABLE, 1
	LOOP lblSendReportMainLoop
	
	POP AX
	POP BX
	POP CX
SendReport ENDP

;====================================
; FINITE STATE MACHINE
;====================================
FiniteStateMachine PROC
	PUSHALL
	
	; buying states
	CMP	STATE_PROGRAMMING_CURRENT, STATE_READY
	JZ	lblStateReady
	
	CMP	STATE_PROGRAMMING_CURRENT, STATE_SEARCH_PRODUCT
	JZ	lblStateSearchProduct
	
	CMP STATE_PROGRAMMING_CURRENT, STATE_ENTER_QTY
	JZ	lblStateEnterQty
	
	CMP	STATE_PROGRAMMING_CURRENT, STATE_ADD_TO_BILL
	JZ	lblAddToBill
	
	; function states
	CMP STATE_PROGRAMMING_CURRENT, STATE_FUNCTION_SELECT
	JZ	lblFunctionSelect
	
	CMP	STATE_PROGRAMMING_CURRENT, STATE_DISPLAY_SOLD_QTY
	JZ	lblDisplaySoldQty
	
	CMP	STATE_PROGRAMMING_CURRENT, STATE_PRINT_SOLD_QTY
	JZ	lblPrintSoldQty
	
	CMP	STATE_PROGRAMMING_CURRENT, STATE_DISPLAY_TRAFFIC
	JZ	lblDisplayTraffic
	
	CMP	STATE_PROGRAMMING_CURRENT, STATE_OPEN_CASH_REGISTER
	JZ	lblOpenCashRegister
	
	CMP	STATE_PROGRAMMING_CURRENT, STATE_FUNCTION_COMPLETE
	JZ	lblFunctionComplete
; STATE READY	
lblStateReady: 
	call KeyboardGetPressed
	; 'F' of 'f' pressed
	CMP AL, 70	
	JZ	lblStateReady2FunctionSelect
	
	; case digit
	MOV BL, AL
	call IsDigit
	JZ	exit ; if not digit then break
	; in case of digit
	call KeyboardGetPressed
	MOV STATE_PROGRAMMING_CURRENT, STATE_SEARCH_PRODUCT
	MUL PRO_CURRENT, 10
	ADD PRO_CURRENT, AL
	
	call DisplayProductInfo
	JZ exit; break
	
lblStateReady2FunctionSelect:
	MOV STATE_PROGRAMMING_CURRENT, STATE_FUNCTION_SELECT
	JMP	exit

lblStateSearchProduct: 
	call KeyboardGetPressed
	CMP AL, 67 ; case C
	JZ	lblCS
	; case * go to 'enter qty'
	CMP AL, 42 ; case Qty
	JZ	lblEQty
	; negate
	CMP AL, 35 ; case Negative
	JZ	lblNeg
	
	; case digit
	MOV BL, AL
	call IsDigit
	JZ	exit ; if not digit then break
	; in case of digit
	call KeyboardGetPressed
	MOV STATE_PROGRAMMING_CURRENT, STATE_SEARCH_PRODUCT
	MUL PRO_CURRENT, 10
	ADD PRO_CURRENT, AL
	
	call DisplayProductInfo
	
	JMP exit
	
lblCS:	
	call ClearSearch
	JMP exit
lblEQty:
	MOV STATE_PROGRAMMING_CURRENT, STATE_FUNCTION_SELECT
	JMP exit
lblNeg:
	; change sign
	MOV STATE_PROGRAMMING_CURRENT, STATE_ADD_TO_BILL
	MUL CURRENT_QTY, -1
	JMP exit	

lblStateEnterQty: 
	call KeyboardGetPressed
	CMP AL, 67 ; case C
	JZ	lblQty
	; case * go to 'enter qty'
	CMP AL, 43 ; case +
	JZ	lblAddToBillState
	
	; case digit
	MOV BL, AL
	call IsDigit
	JZ	exit ; if not digit then break
	; in case of digit
	call KeyboardGetPressed
	MOV STATE_PROGRAMMING_CURRENT, STATE_ENTER_QTY
	MUL CURRENT_QTY, 10
	ADD CURRENT_QTY, AL
	JMP exit
lblAddToBillState:
	MOV STATE_PROGRAMMING_CURRENT, STATE_ADD_TO_BILL
	call AddToBill
	JMP exit
lblCQty:
	MOV CURRENT_QTY, 0
	JMP exit
lblAddToBill: 
	call AddToBill
	call KeyboardGetPressed
	MOV BX, AX
	call IsDigit
	JZ	checkEqualChar
	MOV STATE_PROGRAMMING_CURRENT, STATE_SEARCH_PRODUCT
	MUL PRO_CURRENT, 10
	ADD PRO_CURRENT, BL
	
	MOV BX, PRO_CURRENT
	
	ADD CURRENT_SUM, 
	JMP exit
checkEqualChar:
	CMP	BX, 61	; '='
	MOV STATE_PROGRAMMING_CURRENT, STATE_READY
	JMP exit
; STATE FUNCTION SELECT 
lblFunctionSelect: 
	call KeyboardGetPressed
	CMP AL, 67 ; return to ready state
	JZ lblSelect2Ready
	; else
	CMP AL, 48 ; 0
	JZ lblSelect2DisplaySold
	; else
	CMP AL, 49 ; 1
	JZ lblSelect2PrintSold
	; else
	CMP AL, 50 ; 2
	JZ lblSelect2Traffic
	; else
	CMP AL, 51 ; 3
	JZ lblSelect2OpenCashRegister
	
lblSelect2Ready:
	MOV STATE_PROGRAMMING_CURRENT, STATE_READY
	MOV VAR_SHOW_CLOCK, 1
	JMP exit
lblSelect2DisplaySold:
	MOV STATE_PROGRAMMING_CURRENT, STATE_READY
	MOV VAR_SHOW_CLOCK, 0
	call SendReport
	JMP exit
lblSelect2PrintSold:
	MOV STATE_PROGRAMMING_CURRENT, STATE_READY
	MOV VAR_SHOW_CLOCK, 1
	call PrintBill
	JMP exit
lblSelect2Traffic:
	MOV STATE_PROGRAMMING_CURRENT, STATE_READY
	MOV VAR_SHOW_CLOCK, 0
	call ShowTotalSum
	JMP exit
lblSelect2OpenCashRegister:
	MOV STATE_PROGRAMMING_CURRENT, STATE_READY
	MOV VAR_SHOW_CLOCK, 1
	call TiltOpen
	JMP exit
	
	
exit:
	POPALL
FiniteStateMachine ENDP	

;====================================
;	Add to bill
;====================================	
AddToBill PROC
	PUSHALL
	
	; set product id
	MOV BX, BILL_CURRENT_SLOT
	MOV AX, BILL_ITEM_ID
	
	MOV AX[BX], PRO_CURRENT 
	
	; set qty
	MOV AX, BILL_QTY
	MOV AX[BX], CURRENT_QTY
	
	; next slot
	INC BX
	MOV BILL_CURRENT_SLOT, BX
	
	; reset current qty
	MOV CURRENT_QTY, 0	
	
	POPALL
AddToBill ENDP
	
;====================================
; Print Bill
;====================================
PrintBill PROC
	PUSHALL
	
SleepUntilIsSet:
	CMP VAR_READY_TO_PRINT, 1
	JNZ	SleepUntilIsSet
	
	
;	wake up and print
;	destonnation
	MOV ES, SEG VAR_LINE_TO_PRINT
	MOV DI, OFFSET VAR_LINE_TO_PRINT
;	make src str
	MOV AX, TOTAL_SUM
	call WordToString
	
;	result is set in VAR_UTIL_OUTPUT_WORD_TO_STR
	MOV	DS, SEG VAR_UTIL_OUTPUT_WORD_TO_STR
	MOV SI, OFFSET VAR_UTIL_OUTPUT_WORD_TO_STR
	MOV CX, LENGTH VAR_UTIL_OUTPUT_WORD_TO_STR
	
	; set copy string
	
	call StringCopy
	
	; Printer is now busy
	MOV VAR_READY_TO_PRINT, 0
	POPALL
PrintBill ENDP

;============================================================================
; UTILITY PROCEDURES
;============================================================================

;===================================
;	Show error message
;===================================
ShowError PROC
	PUSH AX
	PUSH DS
	PUSH SI
	PUSH CX
	PUSH ES
	PUSH DI

	MOV AX, SEG MSG_ERROR
	MOV DS, AX
	MOV SI, OFFSET MSG_ERROR
	MOV CX, LENGTH MSG_ERROR
	
	MOV AX, SEG MATRIX_DISPLAY
	MOV ES, AX
	MOV DI, OFFSET MATRIX_DISPLAY
	
	CLD
	REP MOVSB

	POP DI
	POP ES
	POP CX
	POP SI
	POP DS
	POP AX

	RET
ShowError ENDP
;====================================
; Show Total Sum
;====================================
ShowTotalSum PROC
	PUSHALL
	
	MOV AX, TOTAL_SUM
	call WordToString
	
	MOV DS, SEG VAR_UTIL_OUTPUT_WORD_TO_STR
	MOV SI, OFFSET VAR_UTIL_OUTPUT_WORD_TO_STR
	
	call PrintMessage
	
	POPALL
ShowTotalSum ENDP

;====================================
; Clear search
;====================================
ClearSearch PROC
call ClearScreen
MOV PRO_CURRENT, 0			; clear selected product
MOV VAR_SHOW_CLOCK, 1			; show clock from now on
ClearSearch ENDP
;====================================
; Display product info
; Uses PRO_CURRENT
;====================================
DisplayProductInfo PROC
	PUSH AX
	PUSH BX
	PUSH CX
	PUSH SI
	PUSH DI
	PUSH ES
	PUSH DS
	
	; show product name
	
	;	init counter
	MOV CX, PRO_NAME_SIZE
	
	;	selected product
	MOV DS, SEG PRO_NAMES
	MOV SI, OFFSET PRO_NAMES
	ADD SI, PRO_CURRENT
	
	;	set destination
	MOV ES, SEG MATRIX_DISPLAY
	MOV	DI, OFFSET MATRIX_DISPLAY
	
	; copy name
	call StringCopy
	
	; init counter for price
	MOV	CX, 4
	
	MOV BX, SELETED_PRODUCT
	MOV AX, PRO_PRICES[BX]
	
	call ByteToString
	
	;	set src
	MOV DS, SEG VAR_UTIL_OUTPUT_BYTE_TO_STR
	MOV SI, OFFSET VAR_UTIL_OUTPUT_BYTE_TO_STR
	
	; ES still the same
	MOV DI, OFFSET MATRIX
	ADD DI, PRO_NAME_SIZE
	
	call StringCopy
	
	POP DS
	POP ES
	POP DI
	POP SI
	POP	CX
	POP	BX
	POP	AX
DisplayProductInfo ENDP

;====================================
; si = source offset
; di = destination offset
; ds, es for segments
; count in cx
StringCopy PROC 
	CLD
	REP MOVSB
	RET
StringCopy ENDP
;====================================

;====================================
; character in bl
; result in al
IsDigit PROC
	MOV AL, 0
	CMP BL, 'A'
	JL lblIsDigitEnd
	CMP BL, 'z'
	JG lblIsDigitEnd
	MOV AL, 1
lblIsDigitEnd:
	RET
IsDigit ENDP

;====================================
IsAlpha PROC
	MOV AL, 0
	CMP BL, '0'
	JL lblIsAlphaEnd
	CMP BL, '9'
	JG lblIsAlphaEnd
	MOV AL, 1
lblIsAlphaEnd:
	RET
IsAlpha ENDP

;====================================
IsAlphaNum PROC
	MOV AL, 0
	CMP BL, '0'
	JL lblIsAlphaNumEnd
	CMP BL, 'z'
	JG lblIsAlphaNumEnd
	MOV AL, 1
lblIsAlphaNumEnd:
	RET
IsAlphaNum ENDP

;====================================

; ax - given word
; output in the VAR_UTIL_OUTPUT_WORD_TO_STR
WordToString PROC 
	PUSH AX
	PUSH 10000
	PUSH CX
	PUSH BX
	PUSH DX
	MOV BX, OFFSET VAR_UTIL_OUTPUT_WORD_TO_STR
	MOV CX, 5

lblWordToStringMainLoop:
	MOV DX, AX
	PUSH AX
	MOV AX, [BP-2]
	DIV DX	; divisor
	POP AX
	ADD DL, '0'
	MOV VAR_UTIL_OUTPUT_WORD_TO_STR[BX], DL
	SUB DL, '0'
	INC BX
	PUSH AX
	MOV AX, [BP-2]
	MUL DX
	POP AX
	SUB AX,  DX
	PUSH AX
	MOV AX, 10
	DIV [BP-2]
	POP AX
	LOOP lblWordToStringMainLoop
	POP DX
	POP BX
	POP CX
	SUB SP, 2
	POP AX
	RET
WordToString ENDP


;====================================

; ax - given byte
; output in the VAR_UTIL_OUTPUT_BYTE_TO_STR
ByteToString PROC 
	PUSH AX
	PUSH 10000
	PUSH CX
	PUSH BX
	PUSH DX
	MOV BX, OFFSET VAR_UTIL_OUTPUT_BYTE_TO_STR
	MOV CX, 3

lblByteToStringMainLoop:	
	MOV DX, AX
	PUSH AX
	MOV AX, [BP-2]
	DIV DX	; divisor
	POP AX
	ADD DL, '0'
	MOV VAR_UTIL_OUTPUT_BYTE_TO_STR[BX], DL
	SUB DL, '0'
	INC BX
	PUSH AX
	MOV AX, [BP-2]
	MUL DX
	POP AX
	SUB AX,  DX
	PUSH AX
	MOV AX, 10
	DIV WORD PTR [BP-2]
	POP AX
	LOOP lblByteToStringMainLoop
	POP DX
	POP BX
	POP CX
	SUB SP, 2
	POP AX
	RET
ByteToString ENDP

PROG ENDS


END

















	
